package stone926.mods.more_enchantments.mixins;

import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LightningEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.entity.projectile.TridentEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import stone926.mods.more_enchantments.MoreEnchantmentsMod;
import stone926.mods.more_enchantments.enchantments.DecapitationEnchantment;
import stone926.mods.more_enchantments.enchantments.TenThousandArrowsEnchantment;
import stone926.mods.more_enchantments.enums.ArrowSpawnPosition;
import stone926.mods.more_enchantments.interfaces.IPersistentProjectileEntity;
import stone926.mods.more_enchantments.interfaces.ITrident;
import stone926.mods.more_enchantments.mixins.accessors.TridentAccessor;
import stone926.mods.more_enchantments.util.DamageUtil;
import stone926.mods.more_enchantments.util.EnchantmentUtil;
import stone926.mods.more_enchantments.util.RandomUtil;

import java.util.UUID;

import static stone926.mods.more_enchantments.MoreEnchantmentsMod.BETRAYAL;
import static stone926.mods.more_enchantments.nbt.CustomNbtKeys.TRIDENT.EXTRA_DAMAGE;

@Mixin(TridentEntity.class)
public abstract class TridentEntityMixin extends PersistentProjectileEntity implements TridentAccessor, ITrident, IPersistentProjectileEntity {

  @Shadow
  private ItemStack tridentStack;
  private float extraDamage = 0;

  protected TridentEntityMixin(EntityType<? extends PersistentProjectileEntity> entityType, World world) { super(entityType, world); }

  @Override
  public float getExtraDamage() { return extraDamage; }

  @Override
  public void setExtraDamage(float damage) { extraDamage = damage; }

  @Override
  public UUID getEntityDamage() { return null; }

  @Override
  public void setEntityDamage(UUID uuid) { }

  @Inject(method = "writeCustomDataToNbt", at = @At("HEAD"))
  private void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) {
    nbt.putFloat(EXTRA_DAMAGE, getExtraDamage());
  }

  @Inject(method = "readCustomDataFromNbt", at = @At("HEAD"))
  private void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) {
    if (nbt.contains(EXTRA_DAMAGE)) setExtraDamage(nbt.getFloat(EXTRA_DAMAGE));
  }

  @Inject(method = "onEntityHit", at = @At("RETURN"))
  protected void handleBetrayalEnchantment(EntityHitResult entityHitResult, CallbackInfo ci) {
    if (EnchantmentUtil.hasEnchantment(BETRAYAL, tridentStack)) {
      if (this.getEntityWorld() instanceof ServerWorld server) {
        server.spawnParticles(ParticleTypes.ELECTRIC_SPARK, this.getX(), this.getY() + 1, this.getZ(), 300, 1.5, 1, 1.5, 0);
        server.spawnParticles(ParticleTypes.INSTANT_EFFECT, this.getX(), this.getY() + 1, this.getZ(), 100, 1, 1, 1, 0);
      }
      kill();
    }
  }

  @Inject(method = "onEntityHit", at = @At("HEAD"))
  private void handleLightningRodEnchantment(EntityHitResult entityHitResult, CallbackInfo ci) {
    if (getOwner() instanceof LivingEntity livingEntity && EnchantmentHelper.getLevel(MoreEnchantmentsMod.LIGHTNING_ROD, tridentStack) > 0) {
      BlockPos blockPos = livingEntity.getBlockPos();
      LightningEntity lightningEntity = new LightningEntity(EntityType.LIGHTNING_BOLT, world);
      lightningEntity.setPosition(livingEntity.getX(), livingEntity.getY(), livingEntity.getZ());
      lightningEntity.refreshPositionAfterTeleport(Vec3d.ofBottomCenter(blockPos));
      lightningEntity.setChanneler(livingEntity instanceof ServerPlayerEntity player ? player : null);
      livingEntity.getEntityWorld().spawnEntity(lightningEntity);
      this.playSound(SoundEvents.ITEM_TRIDENT_THUNDER, 5, 1.0F);
    }
  }

  @Inject(method = "onEntityHit", at = @At("HEAD"))
  private void spawnTridentNearEntity(EntityHitResult entityHitResult, CallbackInfo ci) {
    Entity victim = this.getOwner();
    if (victim instanceof LivingEntity l && l.getEntityWorld() instanceof ServerWorld server) {
      int tenThousandArrowsAtShooterLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.TEN_THOUSAND_ARROWS_AT_SHOOTER, l);
      int tenThousandArrowsAtTargetLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.TEN_THOUSAND_ARROWS_AT_TARGET, l);
      if (!isFromEnch()) {
        TenThousandArrowsEnchantment.spawnTridentNearEntity(server, l, entityHitResult.getEntity(), (TridentEntity) (Object) this, tenThousandArrowsAtShooterLvl, ArrowSpawnPosition.SHOOTER);
        TenThousandArrowsEnchantment.spawnTridentNearEntity(server, l, entityHitResult.getEntity(), (TridentEntity) (Object) this, tenThousandArrowsAtTargetLvl, ArrowSpawnPosition.TARGET);
      }
    }
  }

  @Redirect(method = "onEntityHit", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;damage(Lnet/minecraft/entity/damage/DamageSource;F)Z"))
  private boolean damage(Entity entity, DamageSource source, float baseAmount) {
    if (entity.getUuid().equals(getEntityNotDamage())) {
      discard(); return false;
    } else {
      int decapitationLvl = EnchantmentHelper.getLevel(MoreEnchantmentsMod.DECAPITATION, tridentStack);
      if (getOwner() != null && getOwner() instanceof PlayerEntity player && RandomUtil.isPossible(DecapitationEnchantment.getDecapitationProbability(decapitationLvl, entity), random)) {
        DecapitationEnchantment.decapitate(player, entity, true);
        return true;
      } else {
        float amount = getExtraDamage() + baseAmount;
        if (getOwner() != null && getOwner() instanceof LivingEntity l) {
          amount += DamageUtil.getTridentExtraDamageAmount(l, entity, amount);
        }
        return tryForceDamage(entity, source, amount);
      }
    }
  }

}
